/*
 * Copyright (C) 2010 Leandro Aparecido <lehphyro@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.mjl.impl;

import java.util.*;

import com.google.code.mjl.*;
import com.google.code.mjl.formatter.*;

public class LogEvent {

	private static ThreadLocal<MessageFormatter> messageFormatter = new ThreadLocal<MessageFormatter>() {
		@Override
		protected MessageFormatter initialValue() {
			return LogManager.getMessageFormatterFactory().create();
		}
	};

	private final LogAdapter logAdapter;
	private final long time;
	private final String threadName;
	private final String categoryName;
	private final Log.Level level;
	private final String formatMessage;
	private final Object[] params;
	private final Throwable throwable;

	private LogEvent(String threadName, LogAdapter logAdapter, String categoryName, Log.Level level, String formatMessage, Object[] params, Throwable throwable) {
		this.time = System.currentTimeMillis();
		this.threadName = threadName;
		this.logAdapter = logAdapter;
		this.categoryName = categoryName;
		this.level = level;
		this.formatMessage = formatMessage;
		this.params = params;
		this.throwable = throwable;
	}

	public long time() {
		return time;
	}

	public String threadName() {
		return threadName;
	}

	public LogAdapter logger() {
		return logAdapter;
	}

	public String categoryName() {
		return categoryName;
	}

	public Log.Level level() {
		return level;
	}

	public String formatMessage() {
		return formatMessage;
	}

	public Object[] params() {
		return params;
	}

	public Throwable throwable() {
		return throwable;
	}

	public String message() {
		if (params == null || params.length == 0) {
			if (LogManager.isPrependMessageCodes()) {
				return "[" + generateCode() + "] " + formatMessage;
			} else {
				return formatMessage;
			}
		} else {
			if (LogManager.isPrependMessageCodes()) {
				return "[" + generateCode() + "] " + messageFormatter.get().format(formatMessage, params);
			} else {
				return messageFormatter.get().format(formatMessage, params);
			}
		}
	}

	public String generateCode() {
		char[] cs = formatMessage.toCharArray();
		int size = LogManager.getMessageCodeSize();
		int sizeCharacters = size * 2 / 3;

		char[] result = new char[size];
		Arrays.fill(result, '0');

		int letterIndex = 0;
		int digitIndex = sizeCharacters;

		for (int i = 0; i < cs.length && letterIndex < size; i++) {
			if (Character.isLetterOrDigit(cs[i])) {
				result[letterIndex++] = Character.toUpperCase(cs[i]);
			}
			int wordSize;
			for (wordSize = 0; i < cs.length && cs[i] != ' '; wordSize++, i++) {
				;
			}
			if (digitIndex < size) {
				result[digitIndex++] = Character.forDigit(wordSize, 10);
			}
		}

		return String.valueOf(result);
	}

	@Override
	public String toString() {
		return String.format("LogEvent [categoryName=%s, formatMessage=%s, level=%s, logAdapter=%s, params=%s, threadName=%s, throwable=%s, time=%s]", categoryName, formatMessage, level, logAdapter,
				Arrays.toString(params), threadName, throwable, time);
	}

	public static LogEvent create(LogAdapter internalLogger, String categoryName, Log.Level level, String message, Object[] params) {
		return create(internalLogger, categoryName, level, message, params, null);
	}

	public static LogEvent create(LogAdapter internalLogger, String categoryName, Log.Level level, String message, Object[] params, Throwable throwable) {
		String threadName = Thread.currentThread().getName();
		return create(threadName, internalLogger, categoryName, level, message, params, throwable);
	}

	public static LogEvent create(String threadName, LogAdapter internalLogger, String categoryName, Log.Level level, String formatMessage, Object[] params, Throwable throwable) {
		return new LogEvent(threadName, internalLogger, categoryName, level, formatMessage, params, throwable);
	}
}
